package org.jsola.hr.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.jsola.common.PageKit;
import org.jsola.core.Page;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.EmpInfoStatus;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.IEmpDepDAO;
import org.jsola.hr.dto.EmpDepAddDTO;
import org.jsola.hr.dto.EmpDepUpdateDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.query.EmpDepQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.*;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.*;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.HrConstants.COMPANY_LEVER;

/**
 * @author zhr
 */
@Service("hrEmpDepServiceImpl")
public class EmpDepServiceImpl implements IEmpDepService {

    @Autowired
    private IEmpDepDAO empDepDAO;

    @Autowired
    private IDepInfoService depInfoService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private IEmpPositionService empPositionService;

    @Autowired
    private IPositionInfoService positionInfoService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public EmpDepVO save(EmpDepAddDTO empDepAddDTO, TokenUser tokenUser) {

        ParamException.hasLength(empDepAddDTO.getEmpId(), "员工id不能为空");

        EmpInfoDO empInfoDO = empInfoService.selectDOById(empDepAddDTO.getEmpId(), tokenUser.getSiteId());
        ParamException.notNull(empInfoDO, "员工不存在或已删除");

        if (!empInfoDO.getStatus().equals(EmpInfoStatus.HAS_ENTRY.getValue())
                && !empInfoDO.getStatus().equals(EmpInfoStatus.WAIT_QUIT.getValue())
                && !empInfoDO.getStatus().equals(EmpInfoStatus.WAIT_ENTRY.getValue())) {
            throw new ParamException("员工'" + empInfoDO.getName() + "'不是在职状态,请检查");
        }

        DepInfoVO depInfoVO = depInfoService.selectById(empDepAddDTO.getDepId(), tokenUser.getSiteId());
        ParamException.notNull(depInfoVO, "部门不存在或已删除");

        // 公司级部门
        DepInfoVO companyDep = depInfoService.selectByCode(depInfoVO.getCompanyId(), tokenUser.getSiteId(), depInfoVO.getCompanyId());
        if (Objects.nonNull(companyDep) && empDepAddDTO.getDepId().equals(companyDep.getId())) {
            throw new ParamException("当前员工已在公司下,不需要重复添加");
        }

        // 校验之前的
        EmpDepVO empDepVO = selectByEmpAndDep(
                empDepAddDTO.getCompanyId(),
                empDepAddDTO.getEmpId(),
                empDepAddDTO.getDepId(),
                tokenUser.getSiteId());
        if (!Objects.isNull(empDepVO)) {
            return empDepVO;
        }

        // 转DO
        EmpDepDO empDepDO = empDepAddDTO.to(EmpDepDO.class);
        // 保存
        empDepDO = save(empDepDO, tokenUser.getSiteId(), tokenUser.getUserId());

        return empDepDO.to(EmpDepVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public List<EmpDepVO> batchSave(List<EmpDepAddDTO> empDepAddDTOList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(empDepAddDTOList)) {
            return null;
        }
        List<EmpDepDO> empDepDOList = empDepAddDTOList
                .parallelStream()
                .map(empDepAddDTO -> {
                    EmpDepDO empDepDO = empDepAddDTO.to(EmpDepDO.class);
                    empDepDO.preInsert(tokenUser.getUserId());
                    empDepDO.setSiteId(tokenUser.getSiteId());
                    return empDepDO;
                }).collect(Collectors.toList());
        empDepDAO.insertListAndSetId(empDepDOList);
        return empDepDOList
                .parallelStream()
                .map(empDepDO -> empDepDO.to(EmpDepVO.class))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(EmpDepUpdateDTO empDepUpdateDTO, TokenUser tokenUser) {
        ParamException.hasLength(empDepUpdateDTO.getEmpId(), "员工id不能为空");
        //转DO
        EmpDepDO empDepDO = empDepUpdateDTO.to(EmpDepDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(empDepDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... empDepIds) {
        return deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) empDepIds);
    }


    @Override
    public EmpDepVO selectById(String empDepId, String siteId) {
        EmpDepDO empDepDO = selectDOById(empDepId, siteId);
        if (empDepDO == null) {
            return null;
        }
        return empDepDO.to(EmpDepVO.class);
    }

    @Override
    public List<EmpDepListVO> select(EmpDepQuery empDepQuery, String siteId) {
        List<EmpDepDO> empDepDOList = selectDO(empDepQuery, siteId);
        if (CollectionUtils.isEmpty(empDepDOList)) {
            return empDepDOList == null ? null : new ArrayList<>();
        }
        return empDepDOList.stream()
                .map(empDepDO -> empDepDO.to(EmpDepListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(EmpDepQuery empDepQuery, String siteId) {
        Example example = buildExample(empDepQuery, siteId);
        return empDepDAO.selectCountByExample(example);
    }

    @Override
    public Page<EmpDepListVO> selectPage(EmpDepQuery empDepQuery, String siteId) {
        Example example = buildExample(empDepQuery, siteId);
        Page<EmpDepDO> page = empDepDAO.selectPageByExample(example,
                empDepQuery.getPageNo(),
                empDepQuery.getPageSize());

        return page.to(EmpDepListVO.class);
    }

    @Override
    public EmpDepDO selectDOById(String empDepId, String siteId) {
        return listById(empDepId, siteId);
    }

    @Override
    public List<EmpDepDO> selectDO(EmpDepQuery empDepQuery, String siteId) {
        Example example = buildExample(empDepQuery, siteId);
        return empDepDAO.selectByExample(example);
    }


    /**
     * 根据查询参数，构建example
     *
     * @param empDepQuery 查询参数
     * @param siteId      所属站点id
     * @return example
     */
    private Example buildExample(EmpDepQuery empDepQuery, String siteId) {
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andEqualTo("siteId", siteId);
        if (empDepQuery.getEmpId() != null) {
            example.and().andLike("empId", "%" + empDepQuery.getEmpId() + "%");
        }
        if (empDepQuery.getDepId() != null) {
            example.and().andLike("depId", "%" + empDepQuery.getDepId() + "%");
        }
        if (empDepQuery.getCompanyId() != null) {
            example.and().andEqualTo("companyId", empDepQuery.getCompanyId());
        }
        if (empDepQuery.getGmtCreateStart() != null &&
                empDepQuery.getGmtCreateEnd() != null) {
            example.and().andBetween("gmtCreate", empDepQuery.getGmtCreateStart(),
                    empDepQuery.getGmtCreateEnd());
        }
        if (empDepQuery.getCreateUserId() != null) {
            example.and().andLike("createUserId", "%" + empDepQuery.getCreateUserId() + "%");
        }
        // 排序
        ExampleKit.setExampleOrder(example, empDepQuery.getOrders());
        return example;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public Integer empDistribute(EmpDepAddDTO empDepAddDTO, TokenUser tokenUser) {
        // 要分配的部门
        String depId = empDepAddDTO.getDepId();

        // 要分配到该部门的员工
        List<String> empIdList = empDepAddDTO.getEmpIdList();
        empIdList = CollectionUtils.isEmpty(empIdList) ? new ArrayList<>() : empIdList;

        if (StringUtils.isNotEmpty(empDepAddDTO.getEmpId())) {
            empIdList.add(empDepAddDTO.getEmpId());
        }
        if (CollectionUtils.isEmpty(empIdList)) {
            throw new ParamException("没有需要分配的人员");
        }
        int upCount = 0;
        List<EmpInfoDO> empInfoDOList = empInfoService.selectDoByIds(empIdList);
        Map<String, EmpInfoDO> empInfoDOMap = empInfoDOList.parallelStream().collect(Collectors.toMap(BaseDO::getId, v -> v));
        for (String empId : empInfoDOMap.keySet()) {
            // 更新员工的部门信息
            EmpInfoDO empInfoDO = empInfoDOMap.get(empId);
            // 部门Id
            String preJoinDep = empInfoDO.getPreJoinDep();
            if (StringUtils.isBlank(preJoinDep)) {
                empInfoDO.setPreJoinDep(empDepAddDTO.getDepId());
            } else {
                if (!preJoinDep.contains(empDepAddDTO.getDepId())) {
                    empInfoDO.setPreJoinDep(preJoinDep + "," + empDepAddDTO.getDepId());
                }
            }
            empInfoService.updateByIdSelective(empInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
            EmpDepAddDTO empDepAdd = new EmpDepAddDTO();
            empDepAdd.setCompanyId(empInfoDO.getCompanyId());
            empDepAdd.setEmpId(empId);
            empDepAdd.setDepId(depId);
            // 保存
            save(empDepAdd, tokenUser);
            upCount++;
        }

        return upCount;
    }

    /**
     * 查询用户与部门的关系
     *
     * @param empIdList 用户id集合
     * @param siteId    站点
     * @return 用户与部门的关系
     */
    private List<EmpDepDO> getEmpDepListByEmpIds(List<String> empIdList, String siteId) {
        if (CollectionUtils.isEmpty(empIdList)) {
            return new ArrayList<>();
        }
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andIn("empId", empIdList)
                .andEqualTo("siteId", siteId);

        return empDepDAO.selectByExample(example);
    }


    @Override
    public List<EmpDepDO> getEmpByExample(Example example, String siteId) {
        example.and().andEqualTo("siteId", siteId);
        return empDepDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void deleteByCompanyId(String companyId, TokenUser tokenUser) {
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andEqualTo("companyId", companyId)
                .andEqualTo("siteId", tokenUser.getSiteId());

        empDepDAO.deleteByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void publishEmpDepList(List<EmpDepAddDTO> empDepAddList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(empDepAddList)) {
            return;
        }
        // 删除某个员工所有的
        String empId = empDepAddList.get(0).getEmpId();
        deleteByEmpId(empId, tokenUser);

        empDepAddList.forEach(
                empDepAddDTO -> {
                    EmpDepVO empDepVO = selectByEmpAndDep(
                            empDepAddDTO.getCompanyId(),
                            empDepAddDTO.getEmpId(),
                            empDepAddDTO.getDepId(),
                            tokenUser.getSiteId());

                    // 没有就新增,有就这些参数,不用修改了
                    if (Objects.isNull(empDepVO)) {
                        save(empDepAddDTO, tokenUser);
                    }
                }
        );
        String depIds = empDepAddList.parallelStream().map(EmpDepAddDTO::getDepId).distinct().collect(Collectors.joining(","));
        EmpInfoDO empInfoDO = new EmpInfoDO();
        empInfoDO.setId(empId);
        empInfoDO.setPreJoinDep(depIds);
        empInfoService.updateByIdSelective(empInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    /**
     * 删除某个员工的
     *
     * @param empId     员工id
     * @param tokenUser 当前用户
     */
    private void deleteByEmpId(String empId, TokenUser tokenUser) {
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("siteId", tokenUser.getSiteId());

        List<EmpDepDO> empDepList = empDepDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empDepList)) {
            return;
        }

        String[] ids = empDepList.stream()
                .map(BaseDO::getId).toArray(String[]::new);
        deleteByIds(tokenUser, ids);
    }

    @Override
    public EmpDepVO selectByEmpAndDep(String companyId, String empId, String depId, String siteId) {
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("depId", depId)
                .andEqualTo("companyId", companyId)
                .andEqualTo("siteId", siteId);
        EmpDepDO empDepDO = empDepDAO.selectOneByExample(example);
        if (Objects.isNull(empDepDO)) {
            return null;
        }
        return empDepDO.to(EmpDepVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void deleteByEmpDep(String companyId, String empId, String depId, TokenUser tokenUser) {
        if (StringUtils.isEmpty(companyId) ||
                StringUtils.isEmpty(empId) ||
                StringUtils.isEmpty(depId) ||
                StringUtils.isEmpty(tokenUser.getSiteId())) {
            return;
        }
        EmpDepVO empDepVO = selectByEmpAndDep(companyId, empId, depId, tokenUser.getSiteId());
        if (!Objects.isNull(empDepVO)) {
            deleteByIds(tokenUser, empDepVO.getId());
        }
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void saveEmpDeps(String companyId, String empId, List<String> depIdList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(depIdList)) {
            return;
        }
        // 删除某个员工原所有的
        deleteByEmpId(empId, tokenUser);
        // 增加新的
        depIdList.forEach(
                depId -> {
                    DepInfoVO depInfoVO = depInfoService.selectById(depId, tokenUser.getSiteId());
                    if (!Objects.isNull(depInfoVO)) {
                        EmpDepAddDTO empDepAddDTO = new EmpDepAddDTO();
                        empDepAddDTO.setCompanyId(companyId);
                        empDepAddDTO.setEmpId(empId);
                        empDepAddDTO.setDepId(depId);
                        // 保存
                        save(empDepAddDTO, tokenUser);
                    }
                }
        );
    }

    @Override
    public List<EmpDepDO> listAllByDepId(String companyId, String siteId, String... depIds) {
        if (depIds == null || CollectionUtils.isEmpty(Arrays.asList(depIds))) {
            return null;
        }
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andIn("depId", Arrays.asList(depIds))
                .andEqualTo("companyId", companyId)
                .andEqualTo("siteId", siteId);

        return empDepDAO.selectByExample(example);
    }

    @Override
    public List<EmpDepDO> listAllByCompany(String companyId, String siteId) {
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andEqualTo("companyId", companyId)
                .andEqualTo("siteId", siteId);

        return empDepDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public Integer deleteEmpByDep(String depId, TokenUser tokenUser, String... empIds) {
        if (empIds == null || empIds.length == 0) {
            return 0;
        }
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andIn("empId", Arrays.asList(empIds))
                .andEqualTo("depId", depId)
                .andEqualTo("siteId", tokenUser.getSiteId());

        List<EmpDepDO> empDepList = empDepDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empDepList)) {
            return 0;
        }
        String[] ids = empDepList.stream()
                .map(BaseDO::getId).toArray(String[]::new);
        // 更新员工信息中部门信息
        List<EmpInfoDO> empInfoDOList = empInfoService.selectDoByIds(Arrays.asList(empIds));
        Map<String, EmpInfoDO> empInfoDOMap = empInfoDOList.parallelStream().collect(Collectors.toMap(BaseDO::getId, v -> v));
        for (String empId : empInfoDOMap.keySet()) {
            // 更新员工的部门信息
            EmpInfoDO empInfoDO = empInfoDOMap.get(empId);
            // 部门Id
            String preJoinDep = empInfoDO.getPreJoinDep();
            // 部门中含有 已删除的部门Id 在字符串中剔除
            if (!StringUtils.isBlank(preJoinDep) && preJoinDep.contains(depId)) {
                String[] split = preJoinDep.split(",");
                empInfoDO.setPreJoinDep(Arrays.stream(split).filter(preDep
                        -> !depId.equals(preDep)).collect(Collectors.joining(",")));
            }
            empInfoService.updateByIdSelective(empInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        return deleteByIds(tokenUser, ids);
    }

    @Override
    public List<EmpDepDO> listAllByEmpId(String companyId, String empId, String siteId) {
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("companyId", companyId)
                .andEqualTo("siteId", siteId);
        return empDepDAO.selectByExample(example);
    }

    @Override
    public List<String> selectEmpIdListByDepId(List<String> depIdList, EmpDepQuery empDepQuery, String siteId) {
        return empDepDAO.selectEmpIdListByDepId(depIdList, empDepQuery, siteId);
    }

    @Override
    public String selectDepByEmpId(String empId, TokenUser tokenUser) {
        Example example = new Example(EmpDepDO.class);
        example.and()
                .andEqualTo("empId", empId);
        List<EmpDepDO> empDepList = empDepDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empDepList)) {
            return null;
        }
        List<String> depIdList = empDepList.stream().map(EmpDepDO::getDepId).collect(Collectors.toList());
        List<DepInfoDO> depInfoList = depInfoService.selectByIds(depIdList, tokenUser);
        if (CollectionUtils.isEmpty(depInfoList)) {
            return null;
        }
        List<String> depNameList = new ArrayList<>();
        for (DepInfoDO depInfoDO : depInfoList) {
            depNameList.add(depInfoDO.getName());
        }
        return String.join(";", depNameList);
    }

    @Override
    public Page<EmpReduceInfoVO> selectEmpByDepId(EmpDepQuery empDepQuery, TokenUser tokenUser) {
        // 找出这个部门下的员工Id
        // 因为这里只能用中间表查 只能查Id, 我怕会有员工删除了中间表没删的情况 所以写了sql  就没用分页查询的方法
        // 因为我写的sql只查询一个字段并且加了索引 查出所有id  再分页 和直接分页 估计性能也不会相差太多
        DepInfoDO depInfoDO = depInfoService.selectById(empDepQuery.getDepId(), tokenUser);
        List<String> empIdList;
        // 如果他选到了公司级部门  则查询这个公司下所有员工
        if (COMPANY_LEVER.equals(depInfoDO.getLevel())) {
            List<EmpInfoVO> empInfoList = empInfoService.selectByCompanyId(empDepQuery.getCompanyId(),
                    empDepQuery.getEmpName(),true, tokenUser);
            if (CollectionUtils.isEmpty(empInfoList)) {
                return new Page<>(empDepQuery.getPageSize(), empDepQuery.getPageNo());
            }
            empIdList = empInfoList.stream().map(EmpInfoVO::getId).collect(Collectors.toList());
        } else {
            if (empDepQuery.getDisplayAll()) {
                // 如果显示全部部门员工则递归找到这个部门及它的子部门Id
                List<String> childrenDepIdList = depInfoService.selectByParentId(
                        Collections.singletonList(empDepQuery.getDepId()), tokenUser.getSiteId());
                empIdList = selectEmpIdListByDepId(childrenDepIdList, empDepQuery, tokenUser.getSiteId());
            } else {
                empIdList = selectEmpIdListByDepId(Collections.singletonList(empDepQuery.getDepId()),
                        empDepQuery, tokenUser.getSiteId());
            }
        }
        if (CollectionUtils.isEmpty(empIdList)) {
            return new Page<>(empDepQuery.getPageSize(), empDepQuery.getPageNo());
        }
        Page<String> empIdPage = PageKit.startPage(empIdList, empDepQuery.getPageNo(), empDepQuery.getPageSize());
        // 拿到分页的员工ID
        List<String> empIdPageList = empIdPage.getEntities();
        // 查询出这十个员工来
        List<EmpReduceInfoVO> empReduceInfoList = empInfoService.selectEmpReduceInfoByIdList(empIdPageList);
        // 因为员工和岗位是一对多 所以还要查岗位  直属部门的话直接用传进来的哪个部门就行了
        List<EmpPositionDO> empPositionList = empPositionService.selectByEmpIdList(empIdPageList, tokenUser.getSiteId());
        Map<String, List<EmpPositionDO>> empPositionMap = empPositionList
                .stream().collect(Collectors.groupingBy(EmpPositionDO::getEmpId));
        // 拿到岗位Id列表
        List<String> positionIdList = empPositionList.stream()
                .map(EmpPositionDO::getPositionId).distinct().collect(Collectors.toList());
        List<PositionInfoDO> positionInfoList = positionInfoService.selectByIdList(positionIdList, tokenUser);
        Map<String, String> positionNameMap = positionInfoList.stream()
                .collect(Collectors.toMap(PositionInfoDO::getId, PositionInfoDO::getName));
        for (EmpReduceInfoVO empReduceInfoVO : empReduceInfoList) {
            List<EmpPositionDO> positionList = empPositionMap.get(empReduceInfoVO.getId());
            if (!CollectionUtils.isEmpty(positionList)) {
                List<String> positionNameList = new ArrayList<>();
                for (EmpPositionDO empPositionDO : positionList) {
                    positionNameList.add(positionNameMap.get(empPositionDO.getPositionId()));
                }
                empReduceInfoVO.setPositionName(String.join(";", positionNameList));
            }
            // 如果是直接查询的公司级部门,部门名称就不能直接赋值当前查询的这个部门了,挨个查把,最多也就查20次
            if (COMPANY_LEVER.equals(depInfoDO.getLevel())) {
                empReduceInfoVO.setDepName(selectDepByEmpId(empReduceInfoVO.getId(), tokenUser));
            } else {
                empReduceInfoVO.setDepName(depInfoDO.getName());
            }
        }
        Page<EmpReduceInfoVO> resultPage = PageKit
                .startPage(empReduceInfoList, empDepQuery.getPageNo(), empDepQuery.getPageSize());
        resultPage.setEntityCount(empIdPage.getEntityCount());
        resultPage.setFirstEntityIndex(empIdPage.getFirstEntityIndex());
        resultPage.setLastEntityIndex(empIdPage.getLastEntityIndex());
        resultPage.setPageCount(empIdPage.getPageCount());
        resultPage.setPageNo(empIdPage.getPageNo());
        resultPage.setPageSize(empIdPage.getPageSize());
        return resultPage;
    }
}




